home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Assassins - Ultimate CD Games Collection 2
/
Assassins 2 - Ultimate Games No. 2 (1995)(Weird Science)[!][Amiga-CD32-CDTV].iso
/
arcade
/
jar
/
src
/
tetrissound.s
< prev
next >
Wrap
Text File
|
1995-01-01
|
34KB
|
1,275 lines
public _stdout
; Simple Sound Engine © Copyright 1990 Software Alchemy All Rights Reserved
; Revision 0.70 Monday January 8 1990 Anselm Hook
; Revision 0.71 Jan 29 90
; Revision 0.80 Feb 2 90
; Audio can be crunched by discarding the lowest 4 bits of data, and then
; overlapping the second half of the data with the first, although data is
; lost by this technique it still sounds pretty good. I didn't do this here.
;
; There are 2 bugs:
;
; 1) I am *still* losing serial data...
; My current hypothesis is that the InternalSYNC routine is
; breaking up packets despite theory to the contrary.
; There is no fix for this except having a proper externally
; supplied SYNC, if I do detect external SYNC I do turn mine off,
; in which case it should work properly, although my keyboard
; doesn't supply a sync to me.
; [I am using a 256 byte circular buffer with raw serial input.]
; search for the flag 'AZUREVIDIAN' to turn internal sync on.
;
; 2) Sounds do not get turned off quickly enough in StopSound and
; as a consequence more channels are in use at any moment than
; required, this gives a clipping effect to playback. This doesn't
; happen in the sequencer, but only in the playback routine.
;
; Another problem is that the playback runs at level 6, which means
; sound-interupts may not happen as quickly as possible. I know
; this causes a problem because when I had the level 6 just set a
; flag, and ran the playback from non-interupt mode, it was flawless.
; [Ie: sound-interupts also help stop sounds and free up channels].
;
; You will notice the effect in the game music, it sounds like it
; goes into solo mode for small sequences.
; Note I spell Interupt with one 'R', I forward this motion.
;
; 64 Instruments currently, 16 Midi Channels (ie: 16 noises at any time-slice)
; NOTE: Pass sound # to play in D0, must be multiple of 16.
; The MusicSequencer relys on an external MIDI SYNC.
; But the MusicEngine, which plays your finished product does its own timing.
;
;
; I think the following two features would be nice to have,
; although I am not going to program them myself:
;
; #1)
; overlay mode erases any notes colliding with same channel at same sync time
; overlay mode is good for final touch ups, and for multiple keyboards.
; An entire music score could be built with one pass of the sequencer.
; #2)
; erase mode erases any notes on same channel right to end of sequence
; erase mode is good for a single keyboard enviroment where multiple
; passes of the sequencer (on diff tracks) are used to build a music score.
;
blanks on ; Bezerk Aztek declarations
MIDI_NULL equ $00 ; Supported Midi Commands
MIDI_NOTE equ $90
MIDI_NOTEOFF equ $A0
MIDI_ENDSONG equ $FC
MIDI_SYNC equ $F8
MIDI_FORCERTS equ $ef ; Internal Commands
MIDI_NOTECHANGE equ $B0 ; Unsupported Commands
MIDI_CHANGESAM equ $C0
MIDI_SONGSTART equ $FA
MAXSAM equ 64 ; # of Instruments Maximum Allowed...
BUFFERSIZE equ $20000 ; Memory of Music Score
public SoundEngineStart ; Warm up Audio Engine
public SoundEngineStop ; Stop Audio Engine
public SoundPlay ; Play Requested Sound D0>>4
public SoundStop ; Stop Requested Sound D0>>4
public SoundGetHunk
public SoundDecodeHunk
public SoundReleaseHunk
public MusicEngineStart
public MusicEngineStop
public MusicEnginePause
public MusicEngineUnPause
public MusicSequencer
public JustPlayIt
public StopTheMusic
public Cases ;(use for debugging "optimizations" )
public MCases
;**************************************************************************
; stand alone module, just compile and link to itself only, and run.
StandAlone
clr.l d0
Borrow lea skipme(pc),a0 ;(flag, sequencer or player)
move.l d0,(a0)
lea one(pc),a0
bsr dumpmess
move.l #BUFFERSIZE,d0
bsr MusicMem ; Get and Prep Mem
beq.s StandX3
lea two(pc),a0
bsr dumpmess
lea FileName(pc),a0
bsr SoundGetHunk ; A0 = Pointer to File
beq.s StandX2
lea fh(pc),a1
move.l a0,(a1)
move.l a0,-(a7)
lea three(pc),a0
bsr dumpmess
move.l (a7)+,a0
bsr SoundDecodeHunk ; Decode from A0
beq.s StandX
lea seven(pc),a0 ; say LOAD SCORE
bsr dumpmess
lea FileName2(pc),a0
move.l a0,d1
bsr FindFileSize ; d0 = size or zero
beq.s noload
move.l MusicBuffer(pc),a0
add.l d0,a0
cmp.l MusicBufferEnd(pc),a0 ; too big?
bcc.s noload
lea FileName2(pc),a1
move.l a1,d1 ; D1 = Name
move.l MusicBuffer(pc),a3 ; A3 = Dest
move.l d0,a2 ; A2 = Size
move.l d0,a5 ; (save size)
bsr LoadFile
beq.s noload
move.l MusicBuffer(pc),a3 ; Advance to end of load
add.l a5,a3
move.b #MIDI_ENDSONG,(a3)+ ; Make sure we have a stop
move.b #MIDI_ENDSONG,(a3)+ ; make sure stop is visible
move.b #MIDI_ENDSONG,(a3)+
lea BufferMerge(pc),a1 ; (dummy, flag merge)
move.l a3,(a1)
lea four(pc),a0 ; Say loaded
bsr dumpmess
bra.s load
noload move.l MusicBuffer(pc),a3
load lea five(pc),a0
bsr dumpmess
move.l skipme(pc),d0
bne JustPlayItCont
; (A3 = End of Song IN & OUT)
btst #7,$bfe001
bne.s 1$
;----------------------------------------------------------------------------
lea didstartit(pc),a0 ; hack
move.b #1,(a0)
bsr SoundEngineStart ; (playback only mode)
move.l MusicBuffer(pc),a0
bsr MusicEngineStart
moveq #24,d0 ; Speed!
bsr MusicEngineUnPause
0$ btst #6,$bfe001
bne.s 0$
bsr MusicEngineStop
bsr SoundEngineStop
bra.s StandX
;----------------------------------------------------------------------------
1$
;----------------------------------------------------------------------------
bsr MusicSequencer ; Play and allow sequencing
;----------------------------------------------------------------------------
; (A3 = End of Song IN & OUT)
; SAVE SCORE
sub.l MusicBuffer(pc),a3
cmp.l #20,a3 ; Don't save if very small
bcs.s StandX
cmp.l #BUFFERSIZE-3,a3 ; bug???
bcc.s StandX
move.b #MIDI_ENDSONG,(a3)+ ; Make sure we have a stop
move.b #MIDI_ENDSONG,(a3)+ ; make sure stop is visible
move.b #MIDI_ENDSONG,(a3)+
move.l a3,a2 ; a2 = size
move.l MusicBuffer(pc),a3 ; a3 = source
lea FileName2(pc),a0
move.l a0,d1 ; D1 = Name
bsr SaveFile
lea six(pc),a0
bsr dumpmess
StandX move.l fh(pc),a1
bsr SoundReleaseHunk
StandX2 move.l MusicBuffer(pc),a1
move.l #BUFFERSIZE,d0
bsr FreeMemory
StandX3 moveq #0,d0
rts
;----------------------------------------------------------------------------
JustPlayIt
bsr SoundEngineStart ; (always start audio at least)
moveq #-1,d0
bra Borrow ; (either comes here or err)
JustPlayItCont
lea didstartit(pc),a0
move.b #1,(a0)
move.l MusicBuffer(pc),a0
bra MusicEngineStart ; Just play song (interupts)
StopTheMusic
bsr SoundEngineStop
move.b didstartit(pc),d0 ; (allow game without music)
beq.s nope
bsr MusicEngineStop ; stop interupts
bra StandX
nope rts
didstartit:
dc.l 0
;----------------------------------------------------------------------------
FileName dc.b 'music.hunk',0,0
even
FileName2 dc.b 'music.score',0,0
even
fh dc.b 'ANDY HOOK '
skipme dc.l 0
;**************************************************************************
SoundEngineStart
move.l #$dff000,a6 ; A6 = Base of Hardware
lea SoundVector(pc),a0
move.l $70,(a0) ; (save)
bsr SoundEngineStop ; (stop any previous activity)
lea SoundInterupt(pc),a0 ; A0 = My Audio Handler
move.l a0,$70 ; Audio Interupt Level 4
move.w #$c780,$9a(a6) ; Enable Audio Interupts
rts
SoundEngineStop
move.l #$dff000,a6 ; A6 = Base of Hardware
move.w #$000f,$96(a6) ; Turn OFF All Audio DMA
move.w #$0780,$9c(a6) ; Clear Audio Interupts Pend
move.w #$0780,$9a(a6) ; Disable Audio Interupts
move.l SoundVector(pc),$70 ; (restore)
bchg #1,$bfe001 ; Audio Filter On/Off
rts
dc.b "As we become less like those we know, we "
dc.b "seem to become more like those we hear of"
;
; Sound Interupts occur only when its time to kill a sound
;
SoundInterupt
move.w #$2780,$dff09a ; Prevent interupt collisions
movem.l d0/d1/a0/a6,-(a7)
lea $dff000,a6
move.w $1e(a6),d0 ; Interupt Request Read
and.w #$0780,d0
moveq #0,d1
move.w d0,d1 ; (save)
lsr.w #7,d1
move.w d1,$96(a6) ; Stop these Audio DMA
bsr SoundPause
move.w d0,$9c(a6) ; Clear these Audio Pending
or.l #$01000010,d1 ; Frequency Fast, Volume Low
lea SoundChannel(pc),a0
btst #0,d1
beq.s 1$
clr.b (a0)
clr.b 4(a0)
move.l d1,$a6(a6)
1$ btst #1,d1
beq.s 2$
clr.b 1(a0)
clr.b 5(a0)
move.l d1,$b6(a6)
2$ btst #2,d1
beq.s 3$
clr.b 2(a0)
clr.b 6(a0)
move.l d1,$c6(a6)
3$ btst #3,d1
beq.s 4$
clr.b 3(a0)
clr.b 7(a0)
move.l d1,$d6(a6)
4$
;eor.w #$8780,d0 (optional)
;move.w d0,$9a(a6)
move.w #$a780,$dff09a
movem.l (a7)+,d0/d1/a0/a6
rte
;
; Pointer to Audio Data
;
SoundRaw dc.l 0
SoundVector dc.l 0
;
; User List of available Sounds
; Format
; Delta-Start.l, Length, Frequency, Volume, Priority, 0,0,0
;
SoundList dcb.w 16*MAXSAM,0
;
; Sound Channel Arbitration Manager
; x = Active Priority, 0 = Available
;
SoundChannel dc.b 0,0,0,0
SoundOwner dc.b 0,0,0,0
SoundExtra dc.b 0,0,0,0 ;<- Extra Info for Note-OFF
dc.b 0,0,0,0
;
; Arbitration system finds best channel to free based on priority
; of sound request. Sounds of the same priority may overrun each other.
; D0 = Requested Sound to Play
; D7 = Off extra-information tag (optional)
SoundPlay
move.w #$2000,$dff09a ; Allow Game Audio Also!!!
lea SoundList(pc),a0
lea (a0,d0.w),a0
move.l 6(a0),d5 ; Pre-get Frequency, Volume
moveq #0,d7 ; (off tag defaults to zero)
bsr SoundPlayTwo
move.w #$a000,$dff09a
rts
SoundPlayTwo
tst.b d5 ; Is Really Note Off?
beq SoundStop
lea SoundList(pc),a0
lea (a0,d0.w),a0 ; A0 = Pointer to Sound Info
move.w 10(a0),d2 ; D2 = Request Priority
moveq #3,d4 ; D4 = Channel # , Loop
moveq #99,d3
1$ move.b SoundChannel(pc,d4.w),d1 ; D1 = Channel Priority
beq.s 3$ ; Available Sound Channel?
cmp.b d1,d2
bcs.s 2$
; cmp.b d2,d1
; bcc.s 2$
move.w d4,d3 ; D3 = Last Available Priority
2$ dbf d4,1$
cmp.b #99,d3
beq.s SoundPlay900 ; No Low Priority Slots Open
move.w d3,d4
3$
; D4 = Channel #0-3
lea SoundChannel(pc),a1
move.b d2,(a1,d4.w) ; D2 = New Sound Priority
move.b d0,4(a1,d4.w) ; D0 = New Sound BackPtr
move.b d7,8(a1,d4.w) ; D7 = Optional Off Tag
swap d7
move.b d7,12(a1,d4.w)
swap d7
moveq #1,d0
lsl.w d4,d0 ; D0 = Hardware Int # 1-2-4-8
move.w d0,d1
lsl.w #7,d1 ; D1 = (0-8)*128=Hardware DMA
; D0 = %0000000000001234
; D1 = %0000012340000000
move.l #$dff000,a6 ; A6 = Base of Hardware
move.w d0,$96(a6) ; Turn off Channel DMA
bsr SoundPause ; Delay against audio clicking
move.w d1,$9a(a6) ; Disable Int
lea $dff0a0,a2 ; A2 = Audio Hardware
lsl.w #4,d4 ; 0-3 * 16
add.w d4,a2
;move.l SoundRaw(pc),a1 ; A1 = Base of Sound Data
;add.l (a0),a1
move.l (a0),a1 ; (jan 29 90, offsets are precomputed now)
move.l a1,(a2) ; Memory
move.w 4(a0),4(a2) ; Length
move.l d5,6(a2) ; Frequency, Volume
bsr SoundPause
or.w #$8000,d0
move.w d0,$96(a6) ; Re-enable DMA
bsr SoundPause ; (wait for started)
; 4(a2) Repeat can go here
move.w d1,$9c(a6) ; Clear Started-Sound Flag
or.w #$8000,d1
move.w d1,$9a(a6) ; Re-enable Interupt
SoundPlay900
rts
SoundPause
move.l d1,-(a7) ; Time Delay to prevent clicks
move.w #320,d1
1$ tst.b d1
dbf d1,1$
move.l (a7)+,d1
rts
;
; Stop a requested sound, scans active sounds and stops if active
; D0 = sound to stop
; D7 = optional sentry/signature/tag/ID for more accurate off
SoundStop
movem.l d0-d3/a0/a6,-(a7)
lea $dff000,a6
; D0 = Sound to stop
lea SoundOwner(pc),a0
moveq #3,d1 ; D1 = Loop Control
1$ cmp.b (a0,d1.w),d0 ; D0 = Found Active Sound?
bne.s 2$
cmp.b 4(a0,d1.w),d7 ; Tags Match?
bne.s 2$
move.l d7,d2
swap d2
cmp.b 8(a0,d1.w),d2
bne.s 2$
clr.b -4(a0,d1.w)
clr.b 0(a0,d1.w)
move.l d1,d2
lsl.b #4,d2 ; D2 = Channel Hardware
lea $dff0a0,a1 ; A1 = Audio Hardware Base
move.w #1,8(a1,d2.w) ; Channel Volume (0 makes pop)
move.w #100,6(a1,d2.w) ; Period (0 makes click)
moveq #1,d2
lsl.b d1,d2 ; D2 = Channel Bit #
move.w d2,$96(a6) ; Stop Audio
bsr SoundPause
lsl.w #7,d2
move.w d2,$9c(a6) ; Clear Interupt Pending
2$ dbf d1,1$
movem.l (a7)+,d0-d3/a0/a6
nyet: rts
;***************************************************************************
;***************************************************************************
; Music Engine written entirely in The Transform Language © 1990 Anselm Hook
; The Music Sequencer was tested using a YAMAHA SHS-10R Midi Synth Keyboard.
;
; For those of you who are already freaked out by all the numbers:
;
; We had a bunch of hardware level stuff to setup the SoundEngine Interupts,
; Now we are going to setup a bunch more interupts for the MusicEngine so
; that it can keep track of time properly. Aside from that the only other
; hardware stuff is the direct SERIAL I/O code....
;
;
; Simply load the music file up and pass start in a0
; end must be terminated with a $fc
;
MusicEngineStart
; a0 = the data!
lea musicstart(pc),a1
move.l a0,(a1)
lea musicpos(pc),a1
move.l a0,(a1)
rts
MusicEngineUnPause
move.b didstartit(pc),d1
beq nyet
lea musicpos(pc),a0 ; Start/Restart Music
move.l musicstart(pc),(a0)
lea MusicServer(pc),a0
bra TimerStartSpeed
MusicEngineStop
MusicEnginePause
move.b didstartit(pc),d1
beq nyet
bra TimerStop
;
; MusicServer, an interupt driven routine which plays your music.
;
MusicServer
btst #1,$bfdd00 ; Clear INT_FLAG + Test Case
beq.s MusicServer100
movem.l d0-d7/a0-a6,-(a7)
bsr MusicServerTwo
movem.l (a7)+,d0-d7/a0-a6
MusicServer100
move.w #$2000,$dff09c ; Clear interupt
rte
MusicServerTwo
lea musicpos(pc),a0
move.l (a0),a3
Music3 move.b (a3)+,d0
Music4 move.b d0,d1
and.w #15,d1
lsr.b #3,d0 ; 0-15 * 2
and.w #30,d0
jmp Cases(pc,d0.w)
Cases bra.s Music3 ; #0
bra.s Music3 ; #1
bra.s Music3 ; #2
bra.s Music3 ; #3
bra.s Music3 ; #4
bra.s Music3 ; #5
bra.s Music3 ; #6
bra.s Music3 ; #7
bra.s Music3 ; #8
bra.s Note ; #9
bra.s Music3 ; #10
bra.s Music3 ; #11
bra.s Music3 ; #12
bra.s Music3 ; #13
rts ; #14 FORCE RTS
Sync cmp.b #12,d1 ; #15 END?
bne.s 1$
move.l musicstart(pc),a3
1$ lea musicpos(pc),a0
move.l a3,(a0)
rts
;---------------------------------------------------------------------------
periods dc.w 856,808,760,720,680,640,604,572,540,508,480,452,428,404,380,360
dc.w 340,320,302,286,270,254,240,226,214,202,190,180,170,160,151,143
dc.w 135,127,120,113,107,101
;---------------------------------------------------------------------------
Note add.b d1,d1 ; d1 = midi channel 0-15
move.w instr(pc,d1.w),d6 ; D6 = Instr of Channel 0-15
move.b -1(a3),d7 ; Signature for Accurate OFF
NoteTwo move.b (a3)+,d0 ; D0 = Note #0-x
cmp.b #$35+64,d0
bcc.s Music4
sub.b #$35,d0 ; Yamaha Lowest Key #
swap d7
move.b d0,d7 ; construct signature id.w
swap d7 ; made of command,channel,note
add.b d0,d0
move.w periods(pc,d0.w),d5 ; D5 =Note=Rate(in upper word)
swap d5
move.b (a3)+,d5 ; D5 = Vol %xxxxxxxxxx111111
lsr.b #1,d5 ; music volume to half, yamaha only!!!
move.w d6,d0
bsr SoundPlayTwo ; Don't corrupt d6/a3-a7
bra.s NoteTwo ; Any more events in packet?
;---------------------------------------------------------------------------
instr dc.w 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224,240
;dc.w 256,17*16,18*16,19*16,20*16,21*16,22*16,23*16,24*16,25*16
;dc.w 26*16,27*16,28*16,29*16,30*16,31*16
;---------------------------------------------------------------------------
NoteChange
add.b d1,d1 ; Channel 0-15
move.b (a3)+,d0 ; Instr # 0-x
lsl #4,d0 ; *16 to for SoundPlay format
lea instr(pc),a0
move.b d0,(a0,d1.w)
bra.s Music3
musicpos dc.l 0
musicstart dc.l 0
;************************************************************************
;************************************************************************
; Utilities for use with music engine
;
; Music sequencer:
; RMB - Restart Music - Will merge your music with previous score.
; LMB - Exit, saves music to disk
;
;Sequencer theory:
;
;Sequencer - Switch thru midi-events
; (get-events)
; note-on
; play, store
; pulse
; read bufferA till pulse
; play,store (overlay/erase mode)
; write pulse
;
;play - call music player manually.
;store - write bufferB
;
;************************************************************************
MusicSequencer
bsr MusicRestart
bsr SerialStart
bsr SoundEngineStart
lea InternalSync(pc),a0
bsr TimerStart
bsr MusicIn
bsr TimerStop
bsr SoundEngineStop
bra SerialStop
;************************************************************************
;************************************************************************
;
; Music In Store, main routine
; Watches for user restart/quit input
; Reads raw-serial data, plays and stores it (till sync)
; Upon sync, merges in old data
; adds sync and loops...
;
Unknown
MusicIn
btst #6,$bfe001 ; LMB
bne.s 1$
rts
1$ btst #7,$bfe001 ; RMB (#6 is port #2 bytheway)
bne.s 2$ ; (remember to refresh sensors)
0$ move.w #$ff0,$dff180 ; YELLOW
btst #7,$bfe001 ; (must release also)
beq.s 0$
bsr MusicRestart
2$ bsr MidiIn ; Wait for Midi Event
MusIn2 move.b d0,d1 ; D1 = Channel + Command
lsr.b #3,d0 ; D0 = Command
and.w #30,d0
jmp MCases(pc,d0.w)
MCases bra.s Unknown
bra.s Unknown
bra.s Unknown
bra.s Unknown
bra.s Unknown
bra.s Unknown
bra.s Unknown
bra.s Unknown
bra.s Unknown
bra.s MNote ; #9
bra.s Unknown ; #10
bra.s Unknown ; #11
bra.s Unknown ; #12
bra.s Unknown ; #13
bra.s Unknown ; #14
bra MSync ; #15
;
; Store and play user input
;
MNote cmp.l MusicBufferEnd(pc),a3
bcc.s MEnd
; within this sync I can receive any other packet
; or more of these (notes)
; if I do not recognize the packet I should pass it back to the
; main routine for it to determine the packet type.
bsr MidiIn ; D0 = Note #
cmp.b #$35,d0 ; Not my packet
bcs.s MusIn2
cmp.b #$35+64,d0 ; Not my packet
bcc.s MusIn2
move.b d0,d2
bsr MidiIn ; D0 = Volume
cmp.b #$41,d0 ; Transmission bug???
bcc.s MusIn2
tst.b d1 ; Command+Channel (once only)
beq.s 1$
move.b d1,d7 ; D7 = Off Signature, dont hurt me!
move.b d1,(a3)+
and.w #15,d1
add.b d1,d1
lea instr(pc),a0
move.w (a0,d1.w),d6 ; D6 = Instrument # (dont damage)
1$ clr.b d1
; whenever we play a note it goes to one of the four channels, now we are
; going to pass a note off for that note, but we may inadvertantly be turning
; off another note which happens to be using that channel dma. To actually
; turn off the proper note we must be able to positively identify that
; particular note and midi source channel.
; so remembering a sound involves remembering its host frequency and channel.
;
; whenever we do a midi-change to a specific channel we must clear all note
; offs for that channel; so that a note off for an old instrument doesn't
; affect any new ones.
;
move.b d2,0(a3) ; Note #0-x
move.b d0,1(a3) ; Volume
move.b #MIDI_FORCERTS,2(a3) ; (return to me)
move.w d1,-(a7)
bsr NoteTwo ; A3+=3, PASS D6!!!
move.w (a7)+,d1
subq.w #1,a3 ; Delete FORCE_RTS
bra.s MNote
MEnd move.w #$f00,$dff180
bra MusicIn
;
; We've played user input, now merge in old score (of this sync).
;
; We install a fake handler and pass old data as fresh input
; The full capacity of the MusicSequencer can be applied to old data now.
; The only thing we do is catch the old sync and abort at that time.
; (Thus the old data timing is run off new external syncs also)
;
MSync move.w #$ff01,$dff034 ; Hardware Refresh for RMB
cmp.b #$f8,d1
bne Unknown
cmp.l MusicBufferEnd(pc),a3
bcc.s MEnd
lea ReadFn(pc),a0 ; On pass #2?
lea Sinking(pc),a1
cmp.l (a0),a1
beq.s MSync2
move.l (a0),4(a0) ; Save Old Fn
move.l a1,(a0)
bra MusicIn ; Eat Fake Events till SYNC
MSync2 move.l 4(a0),(a0)
move.b #MIDI_SYNC,(a3)+
bra MusicIn
Sinking move.b #MIDI_SYNC,d0
cmp.l a3,a4 ; New Overran Old ???
bcs.s SyncHit
cmp.l MusicBufferEnd(pc),a4 ; Read Hit end
bcc.s SyncHit
move.b (a4)+,d0 ; Read Old Music
SyncHit rts
MidiIn move.l ReadFn(pc),a0
jmp (a0)
;***********************************************************************
dc.b "When you find yourself between "
dc.b "the Devil and the deep blue sea "
MusicRestart
lea ReadFn(pc),a0
lea SerialInWait(pc),a1
move.l a1,(a0)
move.l BufferMerge(pc),a0 ; First Time?
cmp.l #0,a0
beq.s UseOld
; a single buffer has the source being copied back to the dest.
; the trick is that the source must start suitably far away from the dest.
; as a matter of fact the best possible place for the source to be is as
; close to the very end as possible, so overwrite happens last if possible.
move.l MusicBuffer(pc),a1
move.l MusicBufferEnd(pc),a0
cmp.l a3,a1
beq.s 2$
1$ move.b -(a3),-(a0) ; Copy up to end...
cmp.l a3,a1
bcs.s 1$ ; A3 will equal *MusicBuffer
2$ move.l a0,a4 ; A4 = SOURCE, A3 = DEST
4$ cmp.l a1,a0
bcc.s 3$
move.b #MIDI_SYNC,-(a0) ; (Erase Rest of Buffer)
bne.s 4$
3$ bra.s Neateh
UseOld move.l MusicBufferEnd(pc),a4 ; A4 = Tracks SOURCE (default)
Neateh move.l MusicBuffer(pc),a3 ; A3 = Tracks DEST
lea BufferMerge(pc),a0
move.l a4,(a0)
rts
MusicMem
move.l d0,a2
bsr AllocateMemory
beq.s MusicMemFail
move.l d0,d1
add.l a2,d1
lea MusicBuffer(pc),a0
move.l d0,(a0)+ ; (start/end)
sub.l #10,d1 ; for checking if *near* end
move.l d1,(a0)
move.l d0,a0
move.b #MIDI_SYNC,d0 ; Prepare recording buffer
1$ move.b d0,(a0)+
cmp.l a0,d1
bne.s 1$
moveq #1,d0
MusicMemFail
rts
ReadFn dc.l 0,0
MusicBuffer dc.l 0 ; Start both buffers
MusicBufferEnd dc.l 0 ; End of both buffers
BufferMerge dc.l 0 ; Start of Source buffer
;****************************************************************************
;****************************************************************************
;some serial in stuff i don't set:
;move.w #115,$dff032 ; BAUD = 31250
;bclr #6,$bfde00 ; CIAB, SPMODE=Input
;move.b #3,$bfd200 ; MODE = Input
;move.b #$ff,$bfd000 ; Turn on everything...
SerialOut
move.w #115,$dff032 ; BAUD = 31250
move.b #$c0,$bfd200 ; MODE = Output
move.b #0,$bfd000
1$ btst #13-8,$dff018 ; Buffer Ready?
beq.s 1$
move.w d0,$dff030 ; Output new byte
rts
SerialInWait
move.w d1,-(a7)
lea SerialPos(pc),a0
move.w (a0),d1 ; My Read Pos
SWait move.b 3(a0),d0 ; Sync Timer Interupt Pos
cmp.b d1,d0 ; Caught up, wait for more
beq.s AddSync
move.b 4(a0,d1.w),d0 ; D0 = Value to Return
cmp.b #MIDI_SYNC,d0 ; External SYNC found!!!
bne.s 1$
move.w #80,-4(a0) ; Disable internal SYNCing
1$ tst.w -4(a0)
beq.s 2$
subq.w #1,-4(a0) ; Try to enable...
2$ addq.b #1,d1 ; Add molulo 255
move.b d1,1(a0) ; Store modulo 255
move.w (a7)+,d1
rts
AddSync
bra.s SWait ; AZUREVIDIAN (remove me)
tst.w -4(a0) ; Externally overriden?
bne.s SWait
tst.w -2(a0)
beq.s SWait
4$ subq.w #1,-2(a0)
move.b #MIDI_SYNC,d0
move.w (a7)+,d1
rts
;
; the theory for internal syncs is that the midi-device should dump data
; in packets, such that serialwait should never wait for the rest of a packet
; If this is true then serialwait only waits during interm periods between
; packets. This is a safe time to issue a sync without breaking packets.
;
; In reality I get so many syncs that they just about override everything
; else.
;
SerialInterupt
btst #11-8,$dff01c ; My Interupt?
beq.s 2$
movem.l d0/d1/a0,-(a7)
move.w $dff018,d0 ; Read Byte from Hardware
lea SerialPos(pc),a0 ; Circular buffer
move.w 2(a0),d1 ; Get Buffer Pointer, clean.w
move.b d0,4(a0,d1.w) ; Store data byte to buffer
addq.b #1,d1 ; Advance pointer Modulo 255
move.b d1,3(a0) ; store pointer Modulo 255
movem.l (a7)+,d0/d1/a0
2$ move.w #$0800,$dff09c ; Clear INTF_RBF in INTREQ
rte
dc.w 0 ; Sync Disable Time Flag
dc.w 0 ; Sync Flag
SerialPos dc.w 0,0 ; Ptr #2,#3
dcb.l 256/4,0 ; Serial Buffer
;****************************************************************************
SerialStart
move.w #$0800,$dff09a
move.w #115,$dff032 ; BAUD = 31250
lea SerialVector(pc),a0
move.l $74,(a0) ; Save Old Vector
lea SerialInterupt(pc),a0
move.l a0,$74
move.w #$0800,$dff09c ; Clear any previous
move.w #$c800,$dff09a ; Turn on Serial Interupt
rts
SerialStop
move.w #$0800,$dff09a
lea SerialVector(pc),a0
move.l (a0),$74
rts
SerialVector dc.l 0
;****************************************************************************
InternalSync
btst #1,$bfdd00 ; Mine?
beq.s 0$
move.l a0,-(a7)
lea SerialPos(pc),a0
addq.w #1,-2(a0) ; One More Sync!!!
move.l (a7)+,a0
0$ move.w #$2000,$dff09c
rte
;************************************************************************
;
; A0 = Handler function, must turn on yourself (poke $dff09a,$8000+$2000)
;
TimerStart
moveq #24,d0 ; Speed!
TimerStartSpeed
move.w #$2000,$dff09a ; Disable external interupt
lea TimerVector(pc),a1
move.l $78,(a1)
move.l a0,$78 ; Set up new interupt server
lea $bfd000,a1
move.b #$7f,$d00(a1) ; Disable CIAB interupt
move.b #204,$400(a1) ; TimerA for 1 millisecond
move.b #2,$500(a1)
move.b d0,$600(a1) ; TimerB for x milliseconds
move.b #0,$700(a1)
move.b #$01,$e00(a1) ; TimerA continuous
move.b #$41,$f00(a1) ; TimerB continuous,count A
move.b #$82,$d00(a1) ; Enable TimerB interupt
move.w #$2000,$dff09c ; Clear Interupt
move.w #$e000,$dff09a ; Enable external interupt
rts
TimerStop
move.w #$2000,$dff09a ; Disable external interupt
move.l TimerVector(pc),$78 ; Restore old timer handler
move.b #0,$bfde00 ; TimerA continuous
move.b #0,$bfdf00 ; TimerB continuous
move.w #$2000,$dff09c ; Clear Interupt
move.w #$a000,$dff09a ; Enable external Interupt
rts
TimerVector: dc.l 0
;****************************************************************************
;****************************************************************************
;
; FORMAT
; 'SAND' , # of Sample.w, # Size of header.w,
; [ HEADER.l ] * # Size of header (including 'SAND' Sentry ID).
; [ SAMPLES ] (whatever memory they take)
;
; I am currently forcing only 16 samples, because of hardcoded memory space.
SoundDecodeHunk
cmp.l #'SAND',(a0)
bne.s DecodeFail
move.w 4(a0),d0 ; # = Samples
cmp.w #999,d0
bcc DecodeFail
move.w 6(a0),d1 ; # = Size of Header
cmp.w #999,d0
bcc DecodeFail
lsl.w #2,d1 ; * 4 (get longwords)
move.l a0,a1
addq.w #8,a0 ; A0 = Start of Offsets
add.w d1,a1 ; A1 = Start of Data
lea SoundList(pc),a2 ; A2 = DEST (List to Build)
cmp.w #MAXSAM,d0 ; Allow 64 Samples Max, 1 Min
bcs.s 1$
moveq #MAXSAM,d0
1$ subq.w #1,d0
bmi.s DecodeFail
2$ move.l a1,d1 ; START + OFFSET = RAW DATA ADDRESS
move.l (a0)+,d2 ; Delta Start
move.l (a0),d3 ; Delta End
add.l d2,d1
sub.l d2,d3
lsr.w #1,d3
move.l d1,(a2) ; Start
move.w d3,4(a2) ; Length in words
move.w #214,6(a2) ; Frequency
move.w #64,8(a2) ; Volume
move.w #2,10(a2) ; Priority
move.w #0,12(a2) ; (pad)
move.w #0,14(a2) ; (pad)
add.w #16,a2 ; Next Sample
dbf d0,2$
moveq #1,d0
rts
DecodeFail
moveq #0,d0
rts
; The Go Anywhere Loader
; You might find the following useful, its a forget-about-memory-management
; file loading subroutine, you just pass it the name and it returns to you
; a pointer to the start. When exiting you call ReleaseFile (with the
; same pointer you got earlier) , which just frees up the memory that it used
; I finally decided that I needed a permanent utility like this...
;***************************************************************************
;***************************************************************************
LVODelay equ -30-168
LVODeviceProc equ -30-144
LVOSeek equ -30-36
LVOOutput equ -30-30
LVOInput equ -30-24
LVOOpen equ -30
LVOWrite equ -30-18
LVORead equ -30-12
LVOClose equ -30-6
LVOLock equ -$54
LVOUnLock equ -$5A
LVOExamine equ -$66
LVOOpenLibrary equ -408
AllocMem equ -30-168
FreeMem equ -30-180
AllocAbs equ -30-$cc
;***********************************************************************
;***********************************************************************
;
; GetFile, Load a file, return pointer to memory, this is a generic utility
; Passed
; A0 = File Name
; Returns
; A0 = Pointer to Start, (Save this Pointer for Close Down)
;
LoadScore
SoundGetHunk
GetFile
move.l a0,a3
bsr GetDosBase ; A6 = Dos Base ***
move.l a3,d1 ; D1 = File Name to Examine
bsr.s FindFileSize ; D0 = File Size
beq.s GetFailure
addq.l #4,d0 ; +4 for internal header
move.l d0,a2 ; A2 = Internal Size
bsr AllocateMemory ; Pass D0=FileSize, return D0=Mem
beq.s GetFailure
move.l a3,d1 ; D1 = File Name to Load
move.l d0,a3 ; A3 = Start
move.l a2,(a3)+ ; A2 = Size (hide it for later freeing)
bsr LoadFile
beq ReleaseFile
move.l a3,a0 ; Success
moveq #1,d0
GetFailure
rts
;***********************************************************************
;
; ReleaseFile, generic utility to work in conjunction with GetFile
; Passed
; A1 = Pointer to GetFile Handle on file
ReleaseScore
SoundReleaseHunk
ReleaseFile
move.l -(a1),d0 ; D0 = Get Header, Size to Release
bsr FreeMemory
moveq #0,d0 ; (return as failure always)
rts
;***********************************************************************
AllocateMemory
; D0 = Amount
move.l #2,d1 ; chip only
move.l a6,-(a7)
move.l 4,a6 ; Exec base
jsr AllocMem(a6)
move.l (a7)+,a6
tst.l d0
rts
;***********************************************************************
FreeMemory
; A1 = Where, D0 = Amount
move.l a6,-(a7)
move.l 4,a6
jsr FreeMem(a6)
move.l (a7)+,a6
rts
;***********************************************************************
;***********************************************************************
;
; Returns A6 = DosBase (I *know* that this will never fail)
;
GetDosBase
move.l 4,a6 ; Open DOS.LIBRARY
lea DosName(pc),a1
moveq #0,d0 ; (any rev #)
jsr LVOOpenLibrary(a6)
move.l d0,a6 ; A6 = DOS.LIBRARY
rts
;***********************************************************************
;***********************************************************************
;
; LoadFile, a generic utility
; Passed
; D1=NAME, A3=DEST, A2=SIZE
; Returns
; success=1/fail=0
;
LoadFile
movem.l d1/a2/a3,-(a7)
bsr GetDosBase
movem.l (a7)+,d1/a2/a3
; D1 = Get FileName
move.l #1005,d2 ; D2 = MODE_OLDFILE LOAD
jsr LVOOpen(a6) ; Open the file
move.l d0,d4 ; d4 is reserved as file ptr
beq LoadFailure
move.l a3,d2 ; D2 = Start
move.l a2,d3 ; D3 = Size
move.l d4,d1 ; D1 = File Pointer
jsr LVORead(a6)
move.l d4,d1 ; FP
jsr LVOClose(a6) ; Close File
moveq #1,d0 ; Success
LoadFailure
rts
;***********************************************************************
;***********************************************************************
; D1=NAME, A3=SOURCE, A2=SIZE
SaveFile
movem.l d1/a2/a3,-(a7)
bsr GetDosBase
movem.l (a7)+,d1/a2/a3
move.l #1006,d2 ; D2 = MODE_NEWFILE SAVE
jsr LVOOpen(a6) ; Open the file
move.l d0,d4 ; d4 is reserved as file ptr
beq LoadFailure
move.l a3,d2 ; D2 = Start
move.l a2,d3 ; D3 = Size
move.l d4,d1 ; D1 = File Pointer
jsr LVOWrite(a6)
move.l d4,d1 ; FP
jsr LVOClose(a6) ; Close File
moveq #1,d0 ; Success
rts
;***********************************************************************
;***********************************************************************
;
; Find the size of a file, a generic utility
; Passed
; D1 = Pointer to File Name (may have to be longword aligned)
; Returns
; D0 = Size of File or 0.
;
;LVOLock equ -$54
;LVOUnLock equ -$5A
;LVOExamine equ -$66
;LVOOpenLibrary equ -408
;
FindFileSize
; D1 = File Name to Examine
moveq #-2,d2
jsr LVOLock(a6) ; D1=Name,D2=Mode
move.l d0,d4
beq.s Failure
lea FileInfoBlock(pc),a2
move.l a2,d0 ; manually force long word alignment
btst #1,d0 ; (i'm not sure if this is necessary but...)
beq.s 1$
addq.w #2,a2
1$
move.l a2,d2
move.l d4,d1
jsr LVOExamine(a6) ; D1=Lock,D2=FIB
move.l d0,-(a7)
move.l d4,d1
jsr LVOUnLock(a6) ; D1=Lock
move.l (a7)+,d0
beq.s Failure
move.l $007C(a2),d0 ; D0 = Size of File
rts
Failure moveq #0,d0 ; D0 = Failure
rts
DosName: dc.b 'dos.library',0
even
;;; cnop 0,4 ; Longword Aligned
;;; ; (actually this may load wrong)
FileInfoBlock dcb.b 268+2,0
;***********************************************************************
;***********************************************************************
dumpmess
movem.l d0-d7/a0-a6,-(a7)
moveq #0,d3
move.b (a0)+,d3 ; D3 = Length
move.l a0,d2 ; D2 = What
bsr GetDosBase
; jsr LVOOutput(a6) ; Get LVOOutput Handle
; move.l d0,d1 ; D1 = Output Handle
move.l _stdout(pc),d1
beq.s dumpnot
jsr LVOWrite(a6) ; LVOWrite
movem.l (a7)+,d0-d7/a0-a6
dumpnot
rts
one dc.b 64-24,'Sequencer attempting to allocate memory',10,0
even
two dc.b 64-24,'Sequencer attempting to load music.hunk',10,0
even
three dc.b 66-24,'Sequencer attempting to decode music.hunk',10,0
even
four: dc.b 58-24,'Sequencer loaded user music.score',10,0
even
five: dc.b 43-24,'Sequencer is happy',10,0
even
six: dc.b 54-24,'Sequencer saved "music.score"',10,0
even
seven: dc.b 60-24,'Sequencer looking for "music.score"',10,0
end
; end of tetrissound.s